package com.andova.glcamera.texture

import android.content.Context
import android.graphics.SurfaceTexture
import android.opengl.GLES30
import com.andova.egl.*
import com.andova.ext.e
import com.andova.ext.getAsset
import com.andova.glcamera.*
import com.andova.glcamera.device.CAMERA_ID_0
import com.andova.glcamera.device.CAMERA_ID_1
import com.andova.glcamera.device.CameraCallback
import com.andova.image.glesInitialize
import java.nio.ByteBuffer

@Deprecated("perfect needed")
internal class BufferRender(
    private val context: Context, private val callback: BufferFrameCallback?,
    private val device: CameraCallback, private val egl: EglTextureCallback
) : OnTextureListener, OnTextureBridger {
    private var oesProgramId = 0
    private val fboIds = IntArray(1)
    private val textureIds = IntArray(3)

    @FrameAction
    private var frameAction = FRAME_ACTION_NORMAL
    private var colorFormat = DEFAULT_COLOR_FORMAT
    private var delayMillisForStart = 0L
    private var isResume = true
    private var mClearColor = true
    private val drawer = Drawer()
    private var frameHandler: GlFrameHThread? = null
    private var mSurfaceTexture: SurfaceTexture? = null

    private var surfaceWidth = 0
    private var previewWidth = 0
    private var surfaceHeight = 0
    private var previewHeight = 0
    private val calculator = Calculator()


    @Volatile
    private var mBuffer: ByteArray? = null
    private var yBuffer: ByteBuffer? = null // TODO:Init
    private var uBuffer: ByteBuffer? = null // TODO:Init
    private var vBuffer: ByteBuffer? = null // TODO:Init
    private var xFlipScale = 1f
    private var yFlipScale = 1f
    private var lazyMillisecond = 0L
    private var recentMillisecond = 0L
    private var rotRadian = if (device.getBuilder()?.cameraId == CAMERA_ID_0) Math.PI / 2 else Math.PI / 2 * 3

    private fun toggleCamera(open: Boolean) {
        if (open) {
            mSurfaceTexture = egl.onLoadTexture(textureIds[0])
            device.openCamera(mSurfaceTexture, device.getBuilder())
        } else device.stopPreview()
    }

    private fun updateViewport(width: Int, height: Int) {
        previewWidth = width
        previewHeight = height
        val viewport = calculator.calcPreviewViewport(previewWidth, previewHeight, surfaceWidth, surfaceHeight)
        GLES30.glViewport(viewport.x, viewport.y, viewport.width, viewport.height)
    }

    override fun pause(clearColor: Boolean) {
        isResume = false
        mClearColor = clearColor
    }

    override fun resume() {
        isResume = true
        egl.onResumeTexture()
    }

    override fun start(delayMillis: Long, format: Int) {
        delayMillisForStart = delayMillis
        colorFormat = when (format) {
            GLES30.GL_RGB -> GLES30.GL_RGB
            GLES30.GL_RGB8 -> GLES30.GL_RGB8
            GLES30.GL_RGBA -> GLES30.GL_RGBA
            GLES30.GL_RGB565 -> GLES30.GL_RGB565
            else -> DEFAULT_COLOR_FORMAT
        }
    }

    override fun action(command: Int) {
        frameAction = command
    }

    override fun handler(handler: GlFrameHThread?) {
        frameHandler = handler
    }

    override fun x(x: Float) {
    }

    override fun y(y: Float) {
    }

    override fun obtainAction(): Int = frameAction
    override fun onPreviewFrame(buffer: ByteArray, format: Int) {
        mBuffer = buffer
    }

    override fun flipScale(xFlipScale: Float, yFlipScale: Float) {
        this.xFlipScale = xFlipScale
        this.yFlipScale = yFlipScale
    }

    override fun rotateRadian(radian: Double) {
        rotRadian = radian
    }

    override fun lazyCallback(sleepMillisecond: Long) {
        if (sleepMillisecond < 0) {
            this.lazyMillisecond = 0
        } else this.lazyMillisecond = sleepMillisecond
    }

    override fun onAvailable(width: Int, height: Int) {
        surfaceWidth = width
        surfaceHeight = height
        drawer.bind(width, height, colorFormat)
        GLES30.glDeleteTextures(textureIds.size, textureIds, 0)
        GLES30.glGenTextures(textureIds.size, textureIds, 0)

        if (delayMillisForStart <= 0) toggleCamera(true) else (egl as? MultiThreadEglHelper)?.sendForegroundMessageDelayed(1, delayMillisForStart)

        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureIds[0])
        GLES30.glTexImage2D(GLES30.GL_TEXTURE_2D, 0, GLES30.GL_LUMINANCE, device.getPreviewSize().width, device.getPreviewSize().height, 0, GLES30.GL_LUMINANCE, GLES30.GL_UNSIGNED_BYTE, null)
        GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_NEAREST)
        GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR)
        GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_S, GLES30.GL_CLAMP_TO_EDGE)
        GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_T, GLES30.GL_CLAMP_TO_EDGE)
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, GLES30.GL_NONE)

        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureIds[1])
        GLES30.glTexImage2D(
            GLES30.GL_TEXTURE_2D,
            0,
            GLES30.GL_LUMINANCE_ALPHA,
            device.getPreviewSize().width / 2,
            device.getPreviewSize().height / 2,
            0,
            GLES30.GL_LUMINANCE_ALPHA,
            GLES30.GL_UNSIGNED_BYTE,
            null
        )
        GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_NEAREST)
        GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR)
        GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_S, GLES30.GL_CLAMP_TO_EDGE)
        GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_T, GLES30.GL_CLAMP_TO_EDGE)
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, GLES30.GL_NONE)

        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureIds[2])
        GLES30.glTexImage2D(
            GLES30.GL_TEXTURE_2D,
            0,
            GLES30.GL_LUMINANCE_ALPHA,
            device.getPreviewSize().width / 2,
            device.getPreviewSize().height / 2,
            0,
            GLES30.GL_LUMINANCE_ALPHA,
            GLES30.GL_UNSIGNED_BYTE,
            null
        )
        GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_NEAREST)
        GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR)
        GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_S, GLES30.GL_CLAMP_TO_EDGE)
        GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_T, GLES30.GL_CLAMP_TO_EDGE)
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, GLES30.GL_NONE)

        oesProgramId = glesInitialize(getAsset(context, "vert_texture_yuv.glsl"), getAsset(context, "frag_texture_y_u_v.glsl"))
        callback?.onSurfaceAvailable(width, height)

        GLES30.glDeleteFramebuffers(fboIds.size, fboIds, 0)
        GLES30.glGenFramebuffers(fboIds.size, fboIds, 0)
        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, fboIds[0])
        GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT0, GLES30.GL_TEXTURE_2D, textureIds[1], 0)
        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, GLES30.GL_NONE)

        val attachments = IntArray(1)
        GLES30.glGetIntegerv(GLES30.GL_MAX_COLOR_ATTACHMENTS, attachments, 0)
        e("max color attachments is ${attachments[0]}")
    }

    override fun onSizeChanged(width: Int, height: Int) {
        surfaceWidth = width
        surfaceHeight = height
        drawer.bind(width, height, colorFormat)
        callback?.onSurfaceSizeChanged(width, height)
    }

    override fun onDestroyed(surfaceTexture: SurfaceTexture?) {
        frameHandler = null
        GLES30.glDeleteProgram(oesProgramId)
        GLES30.glDeleteTextures(textureIds.size, textureIds, 0)
        GLES30.glDeleteFramebuffers(fboIds.size, fboIds, 0)
        drawer.unbind()
        toggleCamera(false)
        callback?.onSurfaceDestroyed(surfaceTexture)
    }

    override fun onTextureFrame(surfaceTexture: SurfaceTexture?) {
        GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)
        try {
            surfaceTexture?.updateTexImage()
        } catch (ex: RuntimeException) {//厂商设备必须要捕获异常
        }
        when (frameAction) {
            FRAME_ACTION_LAZY_BUFFER, FRAME_ACTION_BUFFER -> return
        }

        GLES30.glUseProgram(oesProgramId)
        updateViewport(device.getPreviewSize().width, device.getPreviewSize().height)
        GLES30.glUniform1i(GLES30.glGetUniformLocation(oesProgramId, "uniTexY"), 1)
        GLES30.glUniform1i(GLES30.glGetUniformLocation(oesProgramId, "uniTexU"), 2)
        GLES30.glUniform1i(GLES30.glGetUniformLocation(oesProgramId, "uniTexV"), 3)
        GLES30.glUniform1f(GLES30.glGetUniformLocation(oesProgramId, "rotationRadian"), rotRadian.toFloat())
        // 由于不旋转的时候，是横屏模式，所以在实际竖屏观察时flipScale的两个参数先后分别影响的是y和x，而不是x和y
//            val xFlipScale = if (device.getBuilder()?.cameraDirection == CAMERA_DIRECTION_BACK) -1f else 1f
        GLES30.glUniform2f(GLES30.glGetUniformLocation(oesProgramId, "flipScale"), yFlipScale, xFlipScale)
        device.run {
            if (!isFrameBufferUpdated()) return@run
            GLES30.glActiveTexture(GLES30.GL_TEXTURE1)
            GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureIds[0])
            GLES30.glTexSubImage2D(GLES30.GL_TEXTURE_2D, 0, 0, 0, getPreviewSize().width, getPreviewSize().height, GLES30.GL_LUMINANCE, GLES30.GL_UNSIGNED_BYTE, yBuffer?.position(0))
            GLES30.glActiveTexture(GLES30.GL_TEXTURE2)
            GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureIds[1])
            GLES30.glTexSubImage2D(
                GLES30.GL_TEXTURE_2D,
                0,
                0,
                0,
                getPreviewSize().width / 2,
                getPreviewSize().height / 2,
                GLES30.GL_LUMINANCE_ALPHA,
                GLES30.GL_UNSIGNED_BYTE,
                uBuffer?.position(0)
            )
            GLES30.glActiveTexture(GLES30.GL_TEXTURE3)
            GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureIds[2])
            GLES30.glTexSubImage2D(
                GLES30.GL_TEXTURE_2D,
                0,
                0,
                0,
                getPreviewSize().width / 2,
                getPreviewSize().height / 2,
                GLES30.GL_LUMINANCE_ALPHA,
                GLES30.GL_UNSIGNED_BYTE,
                vBuffer?.position(0)
            )
            device.setFrameBufferUpdated(false)
        }
        drawer.drawFrameVertex(1)
//        callback?.onDrawFrame(surfaceTexture)
    }

    override fun onTaskFrame(surfaceTexture: SurfaceTexture?) {
        when (frameAction) {
            FRAME_ACTION_PREVIEW, FRAME_ACTION_LAZY_PREVIEW -> return
        }
        if (lazyMillisecond > 0L) {
            when (frameAction) {
                FRAME_ACTION_LAZY_NORMAL -> {
                    if (System.currentTimeMillis() - recentMillisecond >= lazyMillisecond) {
                        recentMillisecond = System.currentTimeMillis()
                    }
                }
                FRAME_ACTION_LAZY_ALL, FRAME_ACTION_LAZY_PREVIEW, FRAME_ACTION_LAZY_BUFFER -> {
                    Thread.sleep(lazyMillisecond)
                }
            }
        }
        val data = mBuffer
        data ?: return
        frameHandler?.apply {
            when (device.getCameraId()) {
                CAMERA_ID_0 -> setCamera0Buffer(previewWidth, previewHeight, data, null)
                CAMERA_ID_1 -> setCamera1Buffer(previewWidth, previewHeight, data, null)
            }
        }
        callback?.onFrameData(surfaceWidth, surfaceHeight, previewWidth, previewHeight, data)
    }

    override fun onDelayTask(what: Int) {
        toggleCamera(true)
    }
}